/*
 * Copyright (c) 2022 WildFireChat. All rights reserved.
 */

package cn.wildfire.chat.kit.utils;


import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.lifecycle.Lifecycle;

import cn.wildfire.chat.kit.utils.concurrent.ListenableFuture;
import cn.wildfire.chat.kit.utils.concurrent.SettableFuture;
import cn.wildfire.chat.kit.utils.views.Stub;


public final class ViewUtil {

    private ViewUtil() {
    }

    public static int getKeyboardHeight(Activity activity) {
        // 获取根视图
        View rootView = activity.getWindow().getDecorView();
        Rect rect = new Rect();
        rootView.getWindowVisibleDisplayFrame(rect);

        // 获取屏幕高度
        int screenHeight = rootView.getRootView().getHeight();
        // 计算软键盘高度
        return screenHeight - rect.bottom;
    }

    public static void focusAndMoveCursorToEndAndOpenKeyboard(@NonNull EditText input) {
        int numberLength = input.getText().length();
        input.setSelection(numberLength, numberLength);

        focusAndShowKeyboard(input);
    }

    public static void focusAndShowKeyboard(@NonNull View view) {
        view.requestFocus();
        if (view.hasWindowFocus()) {
            showTheKeyboardNow(view);
        } else {
            view.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
                @Override
                public void onWindowFocusChanged(boolean hasFocus) {
                    if (hasFocus) {
                        showTheKeyboardNow(view);
                        view.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
                    }
                }
            });
        }
    }

    private static void showTheKeyboardNow(@NonNull View view) {
        if (view.isFocused()) {
            view.post(() -> {
                InputMethodManager inputMethodManager = ServiceUtil.getInputMethodManager(view.getContext());
                inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
            });
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends View> T inflateStub(@NonNull View parent, @IdRes int stubId) {
        return (T) ((ViewStub) parent.findViewById(stubId)).inflate();
    }

    public static <T extends View> Stub<T> findStubById(@NonNull Activity parent, @IdRes int resId) {
        return new Stub<>(parent.findViewById(resId));
    }

    public static <T extends View> Stub<T> findStubById(@NonNull View parent, @IdRes int resId) {
        return new Stub<>(parent.findViewById(resId));
    }

    private static Animation getAlphaAnimation(float from, float to, int duration) {
        final Animation anim = new AlphaAnimation(from, to);
        anim.setInterpolator(new FastOutSlowInInterpolator());
        anim.setDuration(duration);
        return anim;
    }

    public static void fadeIn(final @NonNull View view, final int duration) {
        animateIn(view, getAlphaAnimation(0f, 1f, duration));
    }

    public static ListenableFuture<Boolean> fadeOut(final @NonNull View view, final int duration) {
        return fadeOut(view, duration, View.GONE);
    }

    public static ListenableFuture<Boolean> fadeOut(@NonNull View view, int duration, int visibility) {
        return animateOut(view, getAlphaAnimation(1f, 0f, duration), visibility);
    }

    public static ListenableFuture<Boolean> animateOut(final @NonNull View view, final @NonNull Animation animation) {
        return animateOut(view, animation, View.GONE);
    }

    public static ListenableFuture<Boolean> animateOut(final @NonNull View view, final @NonNull Animation animation, final int visibility) {
        final SettableFuture future = new SettableFuture();
        if (view.getVisibility() == visibility) {
            future.set(true);
        } else {
            view.clearAnimation();
            animation.reset();
            animation.setStartTime(0);
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    view.setVisibility(visibility);
                    future.set(true);
                }
            });
            view.startAnimation(animation);
        }
        return future;
    }

    public static void animateIn(final @NonNull View view, final @NonNull Animation animation) {
        if (view.getVisibility() == View.VISIBLE) return;

        view.clearAnimation();
        animation.reset();
        animation.setStartTime(0);
        view.setVisibility(View.VISIBLE);
        view.startAnimation(animation);
    }

    @SuppressWarnings("unchecked")
    public static <T extends View> T inflate(@NonNull LayoutInflater inflater,
                                             @NonNull ViewGroup parent,
                                             @LayoutRes int layoutResId) {
        return (T) (inflater.inflate(layoutResId, parent, false));
    }

    @SuppressLint("RtlHardcoded")
    public static void setTextViewGravityStart(final @NonNull TextView textView, @NonNull Context context) {
        if (isRtl(context)) {
            textView.setGravity(Gravity.RIGHT);
        } else {
            textView.setGravity(Gravity.LEFT);
        }
    }

    public static void mirrorIfRtl(View view, Context context) {
        if (isRtl(context)) {
            view.setScaleX(-1.0f);
        }
    }

    public static boolean isLtr(@NonNull View view) {
        return isLtr(view.getContext());
    }

    public static boolean isLtr(@NonNull Context context) {
        return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
    }

    public static boolean isRtl(@NonNull View view) {
        return isRtl(view.getContext());
    }

    public static boolean isRtl(@NonNull Context context) {
        return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
    }

    public static float pxToDp(float px) {
        return px / Resources.getSystem().getDisplayMetrics().density;
    }

    public static int dpToPx(Context context, int dp) {
        return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5);
    }

    public static int dpToPx(int dp) {
        return Math.round(dp * Resources.getSystem().getDisplayMetrics().density);
    }

    public static int dpToSp(int dp) {
        return (int) (dpToPx(dp) / Resources.getSystem().getDisplayMetrics().scaledDensity);
    }

    public static int spToPx(float sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, Resources.getSystem().getDisplayMetrics());
    }

    public static void updateLayoutParams(@NonNull View view, int width, int height) {
        view.getLayoutParams().width = width;
        view.getLayoutParams().height = height;
        view.requestLayout();
    }

    public static void updateLayoutParamsIfNonNull(@Nullable View view, int width, int height) {
        if (view != null) {
            updateLayoutParams(view, width, height);
        }
    }

    public static void setVisibilityIfNonNull(@Nullable View view, int visibility) {
        if (view != null) {
            view.setVisibility(visibility);
        }
    }

    public static int getLeftMargin(@NonNull View view) {
        if (isLtr(view)) {
            return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin;
        }
        return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin;
    }

    public static int getRightMargin(@NonNull View view) {
        if (isLtr(view)) {
            return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin;
        }
        return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin;
    }

    public static int getTopMargin(@NonNull View view) {
        return ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin;
    }

    public static void setLeftMargin(@NonNull View view, int margin) {
        if (isLtr(view)) {
            ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin;
        } else {
            ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin = margin;
        }
        view.forceLayout();
        view.requestLayout();
    }

    public static void setRightMargin(@NonNull View view, int margin) {
        if (isLtr(view)) {
            ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).rightMargin = margin;
        } else {
            ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).leftMargin = margin;
        }
        view.forceLayout();
        view.requestLayout();
    }

    public static void setTopMargin(@NonNull View view, int margin) {
        ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin = margin;
        view.requestLayout();
    }

    public static void setBottomMargin(@NonNull View view, int margin) {
        ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).bottomMargin = margin;
        view.requestLayout();
    }

    public static int getWidth(@NonNull View view) {
        return view.getLayoutParams().width;
    }

    public static void setPaddingTop(@NonNull View view, int padding) {
        view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), view.getPaddingBottom());
    }

    public static void setPaddingBottom(@NonNull View view, int padding) {
        view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), padding);
    }

    public static void setPadding(@NonNull View view, int padding) {
        view.setPadding(padding, padding, padding, padding);
    }

    public static void setPaddingStart(@NonNull View view, int padding) {
        if (isLtr(view)) {
            view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
        } else {
            view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding, view.getPaddingBottom());
        }
    }

    public static void setPaddingEnd(@NonNull View view, int padding) {
        if (isLtr(view)) {
            view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding, view.getPaddingBottom());
        } else {
            view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
        }
    }

    public static boolean isPointInsideView(@NonNull View view, float x, float y) {
        int[] location = new int[2];

        view.getLocationOnScreen(location);

        int viewX = location[0];
        int viewY = location[1];

        return x > viewX && x < viewX + view.getWidth() &&
            y > viewY && y < viewY + view.getHeight();
    }

    public static int getStatusBarHeight(@NonNull View view) {
        int result = 0;
        int resourceId = view.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = view.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

    public static int getNavigationBarHeight(@NonNull View view) {
        int result = 0;
        int resourceId = view.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = view.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

    public static void hideKeyboard(@NonNull Context context, @NonNull View view) {
        InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
        inputManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }

    /**
     * Enables or disables a view and all child views recursively.
     */
    public static void setEnabledRecursive(@NonNull View view, boolean enabled) {
        view.setEnabled(enabled);
        if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                setEnabledRecursive(viewGroup.getChildAt(i), enabled);
            }
        }
    }

    public static @Nullable
    Lifecycle getActivityLifecycle(@NonNull View view) {
        return getActivityLifecycle(view.getContext());
    }

    private static @Nullable
    Lifecycle getActivityLifecycle(@Nullable Context context) {
        if (context instanceof ContextThemeWrapper) {
            return getActivityLifecycle(((ContextThemeWrapper) context).getBaseContext());
        }

        if (context instanceof AppCompatActivity) {
            return ((AppCompatActivity) context).getLifecycle();
        }

        return null;
    }
}